package com.vecspace.data.model.reflect;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**解析class中的属性名
 * 
 * @version: 1.0
 * @author: wanghua
 */
public class BeanSchema implements Map<String, BeanSchema>{
	private Map<String, BeanSchema> props = new LinkedHashMap<String, BeanSchema>();
	
	private static final BeanSchema ArraySchema = new BeanSchema(); 

	@Override
	public String toString() {
		if(this == ArraySchema)return "[]";
		return super.toString();
	}
	
	public static BeanSchema parse(Class<?> clazz) {
		BeanSchema schema = new BeanSchema();
		if(clazz.isPrimitive()) {
			return null;
		}else if(clazz.getName().startsWith("java.lang.")){
			return null;
		}
		
		Method[] methods = clazz.getMethods();
		for(Method method : methods) {
			if(method.getParameterCount() == 0) {
				String methodName = method.getName();
				if((methodName.startsWith("get") && !methodName.equals("get") && !methodName.equals("getClass") && !methodName.equals("getBytes")&& !methodName.equals("isEmpty")) 
						|| (methodName.startsWith("is") && !methodName.equals("is")) ) {
					String propName = getPropName(methodName);
					Class<?> returnType = method.getReturnType();					
					Type genericReturnType = method.getGenericReturnType();
					if(returnType.isArray()) {
						schema.props.put(propName, null);
					}else if(Collection.class.isAssignableFrom(returnType) || Map.class.isAssignableFrom(returnType)) {
						if(genericReturnType instanceof ParameterizedType) {
							Type actualType = ((ParameterizedType) genericReturnType).getActualTypeArguments()[0];
							if(actualType instanceof TypeVariable) {
								schema.props.put(propName, null);
							}else {							 
								Class<?> entityClass = (Class<?>)actualType;
								BeanSchema schema2 = BeanSchema.parse(entityClass);
								schema.props.put(propName, schema2);
							}
						}else {
							BeanSchema schema2 = BeanSchema.parse((Class<?>)genericReturnType);
							
							schema.props.put(propName, schema2);
						}
					}else {
						if(returnType.isPrimitive()) {
							schema.props.put(propName, null);
						}else if(returnType.getName().startsWith("java.lang.")){
							schema.props.put(propName, null);
						}
						else{
							BeanSchema schema2 = BeanSchema.parse(returnType);							
							schema.props.put(propName, schema2);
						}
					}
				}
			}
		}
		return schema;
	}
			
	private static String getPropName(String methodName) {
		String propName = methodName;
		if(methodName.startsWith("get")) {
			propName = methodName.substring(3);
		}else if(methodName.startsWith("is")) {
			propName = methodName.substring(2);
		}
		propName = propName.substring(0,1).toLowerCase() + propName.substring(1);
		return propName;
	}
	@Override
	public int size() {
		return props.size();
	}

	@Override
	public boolean isEmpty() {
		return props.isEmpty();
	}
	
	@Override
	public boolean containsKey(Object key) {
		return props.containsKey(key);
	}
	
	@Override
	public boolean containsValue(Object value) {
		return props.containsValue(value);
	}

	@Override
	public BeanSchema get(Object key) {
		return props.get(key);
	}

	@Override
	public BeanSchema put(String key, BeanSchema value) {
		return props.put(key, value);
	}

	@Override
	public BeanSchema remove(Object key) {
		return props.remove(key);
	}

	@Override
	public void putAll(Map<? extends String, ? extends BeanSchema> m) {
		props.putAll(m);
	}

	@Override
	public void clear() {
		props.clear();
	}
	
	@Override
	public Set<String> keySet() {		
		return props.keySet();
	}

	@Override
	public Collection<BeanSchema> values() {
		return props.values();
	}

	@Override
	public Set<Entry<String, BeanSchema>> entrySet() {
		return props.entrySet();
	}
}
